package com.fessc.develop.base;

import com.fessc.develop.utils.ExceptionUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@Slf4j
@Setter
@Getter
public class RSATools {
    private String keyAlgorithm = "RSA";
    private String signAlgorithm = "SHA1withRSA";
    private String signLength = "1024";
    private String priCer;
    private String pubCer;
    public static int MAX_ENCRYPT_BLOCK = 117;
    public static int MAX_DECRYPT_BLOCK = 128;

    public RSATools() {

    }

    public RSATools(String priCer, String pubCer) {
        this.priCer = priCer;
        this.pubCer = pubCer;
    }

    public RSATools(String pubCer) {
        this.pubCer = pubCer;
    }

    // RSA验证签名
    public boolean validate(byte[] signatureData, byte[] msgData)
            throws Exception {
        try {
            PublicKey publicKey = GeneratePublicKey();
            Signature sig = Signature.getInstance(signAlgorithm);
            sig.initVerify(publicKey);
            sig.update(msgData);
            return sig.verify(signatureData);
        } catch (Exception e) {
            System.out.println("Exception:" + e.getMessage());
            return false;
        }
    }

    /**
     * RSA签名
     *
     * @param data
     * @return
     */
    public byte[] genValiInfo(byte[] data) {
        try {
            PrivateKey privateKey = GeneratePrivateKey();
            Signature sig = Signature.getInstance(signAlgorithm);
            sig.initSign(privateKey);
            sig.update(data);
            return sig.sign();
        } catch (Exception e) {
            System.out.println("Exception:" + e.getMessage());
        }

        return null;
    }

    /**
     * 两个byte追加
     *
     * @param bt1
     * @param bt2
     * @return
     */
    public byte[] byteAppend(byte[] bt1, byte[] bt2) {

        byte[] rt = null;

        if (bt1 != null)
            rt = new byte[bt1.length + bt2.length];
        else
            rt = new byte[bt2.length];

        int i = 0;
        if (bt1 != null) {
            for (i = 0; i < bt1.length; i++)
                rt[i] = bt1[i];
        }
        for (int j = 0; j < bt2.length; i++, j++)
            rt[i] = bt2[j];
        return rt;
    }

    public byte[] subArray(byte[] arr, int beginIndex, int endIndex) {
        byte[] result = new byte[endIndex - beginIndex];
        for (int i = beginIndex; i < endIndex; i++)
            result[i - beginIndex] = arr[i];
        return result;
    }

    /**
     * 获取公钥
     *
     * @return
     * @throws Exception
     */
    public PublicKey GeneratePublicKey() throws Exception {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(pubCer);
            byte[] encodedpubkey = new byte[fin.available()];
            int keyLength = fin.read(encodedpubkey);
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedpubkey);
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
            return pubKey;
        } catch (Exception e) {
            System.out.println("Exception：" + e.getMessage());
            throw e;
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (Exception e) {
                    System.out.println("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    /**
     * 获取私钥
     *
     * @return
     * @throws Exception
     */
    public PrivateKey GeneratePrivateKey() throws Exception {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(priCer);
            byte[] encodedprikey = new byte[fin.available()];
            int keyLength = fin.read(encodedprikey);
            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(encodedprikey);
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
            return priKey;
        } catch (Exception e) {
            System.out.println("Exception:" + e.getMessage());
            throw e;
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (Exception e) {
                    System.out.println("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }


    // 公钥加密
    public byte[] encrypt1(byte[] content) throws Exception {
        PublicKey publicKey = this.GeneratePublicKey();
        Cipher cipher = Cipher.getInstance("RSA");// java默认"RSA"="RSA/ECB/PKCS1Padding"
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte rt[] = null;
        for (int i = 0; i < content.length; i = i + 117) {
            byte bt[] = cipher.doFinal(subArray(content, i,
                    i + 117 > content.length ? content.length : i + 117));
            rt = this.byteAppend(rt, bt);
        }
        return rt;
    }

    // 私钥解密
    public byte[] decrypt1(byte[] content) throws Exception {
        StringBuffer sb = new StringBuffer();
        PrivateKey privateKey = this.GeneratePrivateKey();
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        for (int i = 0; i < content.length; i = i + 128) {
            sb.append(new String(cipher.doFinal(subArray(content, i,
                    i + 128 > content.length ? content.length : i + 128))));
        }
        return sb.toString().getBytes();
    }

    // 公钥加密
    public String encrypt(String body)
    {
        ByteArrayOutputStream out = null;
        try {
            PublicKey publicKey = this.GeneratePublicKey();
            Cipher cipher = Cipher.getInstance(keyAlgorithm);// java默认"RSA"="RSA/ECB/PKCS1Padding"
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            byte content[] = body.getBytes("utf-8"); // 对方接收是UTF-8编码，要指定
            int inputLen = content.length;
            out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密  
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK)
                {
                    cache = cipher.doFinal(content, offSet, MAX_ENCRYPT_BLOCK);
                }
                else
                {
                    cache = cipher.doFinal(content, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }

            byte[] rt = out.toByteArray();
            out.close();

            BASE64Encoder encode = new BASE64Encoder();
            return encode.encode(rt);
        } catch (Exception e) {
            throw ExceptionUtil.businessException("EE4008");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    System.out.println("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

    // 私钥解密
    public String decrypt(String body)
    {
        ByteArrayOutputStream out = null;
        try {
            PrivateKey privateKey = this.GeneratePrivateKey();
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            BASE64Decoder decode = new BASE64Decoder();
            byte[] b = decode.decodeBuffer(body);
            int inputLen = b.length;
            out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密  
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK)
                {
                    cache = cipher.doFinal(b, offSet, MAX_DECRYPT_BLOCK);
                }
                else
                {
                    cache = cipher.doFinal(b, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] rt = out.toByteArray();
            return new String(rt,"utf-8");  // 对方发送过去是UTF-8编码，要指定回来
        } catch (Exception e) {
            throw ExceptionUtil.businessException("EE4007");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    System.out.println("关闭文件流异常：" + e.getMessage());
                }
            }
        }
    }

}
